let temp1arr = cc.v2();
let temp2arr = cc.v2();
let temp3arr = cc.v2();
let temp4arr = cc.v2();

function isOverlay(proA: { min: number, max: number }, proB: { min: number, max: number }) {
    let min, max;
    if (proA.min < proB.min) {
        min = proA.min;
    } else {
        min = proB.min;
    }

    if (proA.max > proB.max) {
        max = proA.max;
    } else {
        max = proB.max;
    }

    return (proA.max - proA.min) + (proB.max - proB.min) < max - min;
}
export class Intersection {
    static polygonCircle(polygon: cc.Vec2[], circle: { worldPosition: cc.Vec2, worldRadius: number }) {
        let position = circle.worldPosition;
        if (Intersection.pointInPolygon(position, polygon)) {
            return true;
        }
        let sqrtratiud = circle.worldRadius * circle.worldRadius;
        for (let i = 0, len = polygon.length; i < len; i++) {
            let start = i === 0 ? polygon[polygon.length - 1] : polygon[i - 1];
            let end = polygon[i];
            if (Intersection.pointLineDistanceSqr(position, start, end, true) < sqrtratiud) {
                return true;
            }
        }
        return false;
    }


    public static pointLineDistanceSqr(point: cc.Vec2, start: cc.Vec2, end: cc.Vec2, isSegment: boolean) {
        let dx = end.x - start.x;
        let dy = end.y - start.y;
        let d = dx * dx + dy * dy;
        let t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / d;
        let p;

        if (!isSegment) {
            p = cc.Vec2.set(temp1arr, start.x + t * dx, start.y + t * dy);
        }
        else {
            if (d) {
                if (t < 0) p = start;
                else if (t > 1) p = end;
                else p = cc.Vec2.set(temp1arr, start.x + t * dx, start.y + t * dy);
            }
            else {
                p = start;
            }
        }

        dx = point.x - p.x;
        dy = point.y - p.y;
        return dx * dx + dy * dy;
    }

    public static circleCircle(a: { worldPosition: cc.Vec2, worldRadius: number }, b: { worldPosition: cc.Vec2, worldRadius: number }) {
        cc.Vec2.subtract(temp4arr, a.worldPosition, b.worldPosition);
        let dissqrt = temp4arr.magSqr();
        return dissqrt < (a.worldRadius + b.worldRadius) ** 2;
    }

    /**
     * 线段与线段相交
     * @param  {cc.Vec2} a1
     * @param  {cc.Vec2} a2
     * @param  {cc.Vec2} b1
     * @param  {cc.Vec2} b2
     */
    public static lineLine(a1: cc.Vec2, a2: cc.Vec2, b1: cc.Vec2, b2: cc.Vec2) {
        let ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x);
        let ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x);
        let u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);

        if (u_b !== 0) {
            let ua = ua_t / u_b;
            let ub = ub_t / u_b;

            if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
                return true;
            }
        }

        return false;
    }

    /**
     * 线段与矩形相交
     * @param  {cc.Vec2} a1
     * @param  {cc.Vec2} a2
     * @param  {cc.Rect} b
     */
    public static lineRect(a1: cc.Vec2, a2: cc.Vec2, b: cc.Rect) {
        cc.Vec2.set(temp1arr, b.x, b.y);
        cc.Vec2.set(temp2arr, b.x, b.yMax);
        cc.Vec2.set(temp3arr, b.xMax, b.yMax);
        cc.Vec2.set(temp4arr, b.xMax, b.y);

        if (Intersection.lineLine(a1, a2, temp1arr, temp2arr)) {
            return true;
        }
        if (Intersection.lineLine(a1, a2, temp2arr, temp3arr)) {
            return true;
        }
        if (Intersection.lineLine(a1, a2, temp3arr, temp4arr)) {
            return true;
        }
        if (Intersection.lineLine(a1, a2, temp4arr, temp1arr)) {
            return true;
        }
        return false;
    }

    /**
     * 线段与多边形相交
     * @param  {cc.Vec2} a1
     * @param  {cc.Vec2} a2
     * @param  {cc.Vec2[]} b
     */
    static linePolygon(a1: cc.Vec2, a2: cc.Vec2, b: cc.Vec2[]) {
        let length = b.length;
        for (let i = 0; i < length; ++i) {
            let b1 = b[i];
            let b2 = b[(i + 1) % length];
            if (Intersection.lineLine(a1, a2, b1, b2))
                return true;
        }
        return false;
    }

    /**
     * !#en Test rect and rect
     * !#zh 测试矩形与矩形是否相交
     * @method rectRect
     * @param {Rect} a - The first rect
     * @param {Rect} b - The second rect
     * @return {boolean}
     */
    static rectRect(a: cc.Rect, b: cc.Rect) {
        let a_min_x = a.x;
        let a_min_y = a.y;
        let a_max_x = a.x + a.width;
        let a_max_y = a.y + a.height;

        let b_min_x = b.x;
        let b_min_y = b.y;
        let b_max_x = b.x + b.width;
        let b_max_y = b.y + b.height;

        return a_min_x <= b_max_x &&
            a_max_x >= b_min_x &&
            a_min_y <= b_max_y &&
            a_max_y >= b_min_y
            ;
    }

    public static rectPolygon(a: cc.Rect, b: cc.Vec2[]) {
        cc.Vec2.set(temp1arr, a.x, a.y);
        cc.Vec2.set(temp2arr, a.x, a.yMax);
        cc.Vec2.set(temp3arr, a.xMax, a.yMax);
        cc.Vec2.set(temp4arr, a.xMax, a.y);

        // intersection check
        if (Intersection.linePolygon(temp1arr, temp2arr, b))
            return true;

        if (Intersection.linePolygon(temp2arr, temp3arr, b))
            return true;

        if (Intersection.linePolygon(temp3arr, temp4arr, b))
            return true;

        if (Intersection.linePolygon(temp4arr, temp1arr, b))
            return true;

        // check if a contains b
        for (let i = 0, l = b.length; i < l; ++i) {
            if (a.contains(b[i]))
                return true;
        }

        // check if b contains a
        if (Intersection.pointInPolygon(temp1arr, b))
            return true;

        if (Intersection.pointInPolygon(temp2arr, b))
            return true;

        if (Intersection.pointInPolygon(temp3arr, b))
            return true;

        if (Intersection.pointInPolygon(temp4arr, b))
            return true;

        return false;
    }

    public static pointInPolygon(point: cc.Vec2, polygon: cc.Vec2[]) {
        let inside = false;
        let x = point.x;
        let y = point.y;

        // use some raycasting to test hits
        // https://github.com/substack/point-in-polygon/blob/master/index.js
        let length = polygon.length;

        for (let i = 0, j = length - 1; i < length; j = i++) {
            let xi = polygon[i].x, yi = polygon[i].y,
                xj = polygon[j].x, yj = polygon[j].y,
                intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);

            if (intersect) inside = !inside;
        }

        return inside;
    }

    /**
     * 求圆心与多边形最近点
     * @param center 
     * @param a 
     */
    public static getNearestPoint(center: cc.Vec2, a: cc.Vec2[]) {
        let rp = a[0], minDis = cc.Vec2.squaredDistance(center, rp);
        for (let i = 1, len = a.length; i < len; i++) {
            let p2 = a[i], sd = cc.Vec2.squaredDistance(center, p2);
            if (sd < minDis) {
                minDis = sd;
                rp = p2;
            }
        }
        return rp;
    }

    /**
     *分离轴算法，只适合凸多边形
     *
     * @static
     * @param {cc.Vec2[]} a
     * @param {cc.Vec2[]} b
     * @param {cc.Vec2[]} aside
     * @param {cc.Vec2[]} bside
     * @return {*} 
     * @memberof Intersection
     */
    public static satPolygonPolygon(a: cc.Vec2[], b: cc.Vec2[], aside: cc.Vec2[], bside: cc.Vec2[]) {
        for (let i = 0, len = aside.length; i < len; i++) {
            cc.Vec2.set(temp1arr, aside[i].y, -aside[i].x);
            let proA = Intersection.getProjectionPolygon(a, temp1arr);
            let proB = Intersection.getProjectionPolygon(b, temp1arr);
            if (isOverlay(proA, proB)) return false;
        }
        for (let i = 0, ilen = bside.length; i < ilen; i++) {
            cc.Vec2.set(temp1arr, bside[i].y, -bside[i].x);
            let proA = Intersection.getProjectionPolygon(a, temp1arr);
            let proB = Intersection.getProjectionPolygon(b, temp1arr);
            if (isOverlay(proA, proB)) return false;
        }
        return true;
    }

    private static getProjectionPolygon(a: cc.Vec2[], asix) {
        let min = Number.MAX_SAFE_INTEGER, max = -Number.MAX_SAFE_INTEGER;
        for (let ai = 0, al = a.length; ai < al; ai++) {
            let pro = a[ai].dot(asix);
            min = Math.min(min, pro);
            max = Math.max(max, pro);
        }
        return { min, max };
    }

    public static polygonPolygon(a: cc.Vec2[], b: cc.Vec2[]) {
        let i, l;

        // check if a intersects b
        for (i = 0, l = a.length; i < l; ++i) {
            let a1 = a[i];
            let a2 = a[(i + 1) % l];

            if (Intersection.linePolygon(a1, a2, b))
                return true;
        }

        // check if a contains b
        for (i = 0, l = b.length; i < l; ++i) {
            if (Intersection.pointInPolygon(b[i], a))
                return true;
        }

        // check if b contains a
        for (i = 0, l = a.length; i < l; ++i) {
            if (Intersection.pointInPolygon(a[i], b))
                return true;
        }

        return false;
    }

    /**
     * 获取线段相交的点
     * @param  {cc.Vec2} p1
     * @param  {cc.Vec2} p2
     * @param  {cc.Vec2} p3
     * @param  {cc.Vec2} p4
     * @param  {cc.Vec2} out?
     */
    public static getPoint(p1: cc.Vec2, p2: cc.Vec2, p3: cc.Vec2, p4: cc.Vec2, out?: cc.Vec2) {
        out = out || cc.v2();
        let x1 = p1.x, y1 = p1.y;
        let x2 = p2.x, y2 = p2.y;
        let x3 = p3.x, y3 = p3.y;
        let x4 = p4.x, y4 = p4.y;
        let t = ((x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)) / ((x2 - x1) * (y3 - y4) - (x3 - x4) * (y2 - y1));
        return cc.Vec2.set(out, x3 + t * (x4 - x3), y3 + t * (y4 - y3));
    }

    //判断是否为凹多边形状
    public static isConcavePolygon(pos: cc.Vec2[]) {
        let line: cc.Vec2[] = [];
        for (let i = 0, l = pos.length; i < l; ++i) {
            let a1 = pos[i];
            let a2 = pos[(i + 1) % l];
            let linep = cc.v2();
            cc.Vec2.subtract(linep, a1, a2);
            line.push(linep);
        }
        let preValue = line[0].cross(line[1]) >= 0 ? 1 : -1;

        let current:cc.Vec2, next:cc.Vec2;
        let len = line.length;
        for (let i = 1; i < line.length; i++) {
            current = line[i];
            next = line[(i + 1) % len];
            let currentValue = current.cross(next) >= 0 ? 1 : -1;
            if (preValue != currentValue) return true;
            preValue = currentValue;
        }
        return false;
    }
}